package leveldb

import (
	"fmt"

	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/errors"
	"github.com/syndtr/goleveldb/leveldb/filter"
	"github.com/syndtr/goleveldb/leveldb/opt"
)

const (
	minCache   = 4
	minHandles = 8
	dbDir      = "./db"
	dbName     = "ant-agent.db"
)

type Goleveldb interface {
	Has(key string) (bool, error)
	Put(key string, value []byte) error
	Get(key string) ([]byte, error)
	Delete(key string) error
	Close() error
}

type LeveLDB struct {
	db *leveldb.DB
}

func Open() (Goleveldb, error) {
	filePath := fmt.Sprintf("%s/%s", dbDir, dbName)
	db, err := leveldb.OpenFile(filePath, &opt.Options{
		OpenFilesCacheCapacity: minHandles,
		BlockCacheCapacity:     minCache / 2 * opt.MiB,
		WriteBuffer:            minCache / 4 * opt.MiB,
		Filter:                 filter.NewBloomFilter(10),
	})
	if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
		db, err = leveldb.RecoverFile(filePath, nil)
	}
	if err != nil {
		return nil, err
	}

	i := &LeveLDB{
		db: db,
	}
	return i, nil
}

func (i *LeveLDB) Close() error {
	return i.db.Close()
}

func (i *LeveLDB) Has(key string) (bool, error) {
	return i.db.Has([]byte(key), nil)
}

func (i *LeveLDB) Put(key string, value []byte) error {
	return i.db.Put([]byte(key), value, nil)
}

func (i *LeveLDB) Get(key string) ([]byte, error) {
	b, e := i.db.Get([]byte(key), nil)
	if e == errors.ErrNotFound {
		return nil, nil
	}
	return b, e
}

func (i *LeveLDB) Delete(key string) error {
	return i.db.Delete([]byte(key), nil)
}
